let logger = require("pomelo-logger").getLogger(__filename);
let uuid = require('node-uuid');

let Service = function (app) {
    this.app = app;
    this.tables  = [];
    this.gid = 0;
    this.connectionState = {};

    //TODO refactor here, factor out this code
    let self = this;
    setInterval(function () {
        self.tick();
    }, 1000);
    self.doCreateSystemTable();
};

Service.prototype.buildConnectionId = function (frontendId, sessionId) {
    return frontendId + "_" + sessionId;
};

Service.prototype.startCreateTable = function (data, frontendId, sessionId) {
    let result = this.doValidateCreateTableParams(data);
    if (result.resultCode !== 0) {
        return result;
    }
    let room = this.getYuanBaoRoomByMultiplier(data.multiplier);
    if (!room) {
        logger.error('startCreateTable invalid multiplier = ' + data.multiplier);
        return {resultCode: 404, message: "invalid multiplier"};
    }
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        logger.error("startQuickJoin failed. already entering table ... frontendId=" + frontendId +
            " sessionId=" + sessionId);
        return {
            resultCode: 404,
            message: "进桌中，请稍后"
        }
    }
    this.connectionState[connectionId] = 1;
    return {
        resultCode: 0,
        message: "ok",
        room: room
    };
};

Service.prototype.finishCreateTable = function(tableData, playerData, frontendId, sessionId) {
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (!this.connectionState.hasOwnProperty(connectionId)) {
        logger.error("finishJoin failed for invalid connectionState.... frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData));
        return {resultCode: 404, message: "玩家不在线"};
    }
    delete this.connectionState[connectionId];
    if (playerData.resultCode !== 0) {
        logger.error("finishCreateTable for remote create table. message=" + playerData.message +
        " frontendId=" + frontendId + " sessionId=" + sessionId);
        return {resultCode: 404, message: playerData.message};
    }
    if (this.findPlayer(frontendId, sessionId)) {
        logger.error("finishCreateTable failed for player has already in other table...  frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData));
        return {resultCode: 404, message: "玩家在其他桌，不能进入桌子"};
    }
    let player = this.doCreatePlayer(playerData, frontendId, sessionId);
    if (this.findPlayerByUid(player.getUid())) {
        logger.error("finishCreateTable failed for player has already in other table...  frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData) +
            " uid=" + player.getUid());
        return {resultCode: 404, message: "玩家在其他桌，不能进入桌子"};
    }
    if (player.getBalance() < tableData.room.minStack * 2) {
        logger.error("finishCreateTable failed for player do not have enough balance... frontendId = " + frontendId
        + " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData)
        + " tableData=" + JSON.stringify(tableData));
        return {resultCode: 404, message: "元宝不足"};
    }
    tableData['tid'] = uuid.v4();
    let table = this.doCreateYuanBaoTable(tableData);
    table.join(player);
    return {resultCode: 0, message: "ok"};
};

Service.prototype.clearConnectionState = function(frontendId, sessionId) {
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        delete this.connectionState[connectionId];
    }
};

Service.prototype.startQuickJoin = function (frontendId, sessionId, rid) {
    if (!rid) {
        logger.error("zhuoji quickJoinYuanBaoTable rid is empty. rid=" + rid);
        return {resultCode: 404, message: "invalid rid"};
    }
    let table = this.getFirstSpareSystemTableByRid(rid);
    if (!table) {
        logger.error("startQuickJoin failed for invalid rid. rid=" + rid);
        return {resultCode: 404, message: "invalid rid"};
    }
    if (this.findPlayer(frontendId, sessionId)) {
        logger.error('startQuickJoin failed. player already in table');
        return {resultCode: 404, message: "玩家在其他桌，不能进入桌子"};
    }
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        logger.error("startQuickJoin failed. already entering table ... frontendId=" + frontendId +
            " sessionId=" + sessionId);
        return {
            resultCode: 404,
            message: "进桌中，请稍后"
        }
    }
    this.connectionState[connectionId] = 1;
    return {
        resultCode: 0,
        message: "ok",
        table: table
    };
};

Service.prototype.startJoin = function (frontendId, sessionId, code) {
    if (!code) {
        logger.error("startJoin invalid code");
        return { resultCode: 404, message: "桌子序号不正确"};
    }
    let table = this.getPlayerTableByCode(code);
    if (!table) {
        logger.error("startJoin failed. invalid code... frontendId=" + frontendId +
            " sessionId=" + sessionId + " code=" + code);
        return {
            resultCode: 404,
            message: "该房间已关闭，再找找其他房间吧"
        };
    }
    if (this.findPlayer(frontendId, sessionId)) {
        logger.error("startJoin failed. player already in other table ... frontendId=" + frontendId +
            " sessionId=" + sessionId);
        return {
            resultCode: 404,
            message: "玩家在其他桌，不能创建桌子"
        };
    }
    if (table.isFull()) {
        logger.error("startJoin failed. table is full ... frontendId=" + frontendId +
            " sessionId=" + sessionId);
        return {
            resultCode: 403,
            message: "桌子满了"
        };
    }
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        logger.error("startJoin failed. already entering table ... frontendId=" + frontendId +
            " sessionId=" + sessionId);
        return {
            resultCode: 404,
            message: "进桌中，请稍后"
        }
    }
    this.connectionState[connectionId] = 1;
    return {
        resultCode: 0,
        message: "ok",
        table: table
    }
};

Service.prototype.finishJoin = function (table, playerData, frontendId, sessionId) {
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (!this.connectionState.hasOwnProperty(connectionId)) {
        logger.error("finishJoin failed for invalid connectionState.... frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData) +
            " tableId=" + table.getTid());
        return {resultCode: 404, message: "玩家不在线"};
    }
    delete this.connectionState[connectionId];
    if (playerData.resultCode !== 0) {
        logger.error("finishJoin failed... for message=" + playerData.message + " frontendId="
        + frontendId + " sessionId=" + sessionId);
        return {resultCode: 404, message: playerData.message};
    }
    if (table.isFull()) {
        logger.error("finishJoin failed for table is full...  frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData) +
            " tableId=" + table.getTid());
        return {resultCode: 404, message: "invalid table is full"};
    }
    if (this.findPlayer(frontendId, sessionId)) {
        logger.error("finishJoin failed for player has already in other table...  frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData) +
            " tableId=" + table.getTid());
        return {resultCode: 404, message: "玩家在其他桌，不能进入桌子"};
    }
    if (this.tables.indexOf(table) < 0) {
        logger.error("finishJoin failed for table is dismiss... frontendId=" + frontendId +
        " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData) +
            "code=" + table.getCode());
        return {resultCode: 404, message: "桌子已经解散"};
    }
    let player = this.doCreatePlayer(playerData, frontendId, sessionId);
    if (this.findPlayerByUid(player.getUid())) {
        logger.error("finishJoin failed for player has already in other table...  frontendId=" + frontendId +
            " sessionId=" + sessionId + " playerData=" + JSON.stringify(playerData) +
            " tableId=" + table.getTid() + " uid=" + player.getUid());
        return {resultCode: 404, message: "玩家在其他桌，不能进入桌子"};
    }
    table.join(player);
    return {
        resultCode: 0,
        message: "ok"
    };
};

Service.prototype.leaveTable = function (frontendId, sessionId) {
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        logger.warn("player leave table during login");
        delete this.connectionState[connectionId];
    }
    let table = this.findTableByPlayer(frontendId, sessionId);
    let player = null;
    if (table != null) {
        player = table.findPlayer(frontendId, sessionId);
    }
    if (player != null && table.getIsPlaying() && player.isReady()) {
        return {resultCode: 404, message: "正在打牌，无法离开"};
    }
    if (player != null && table != null) {
        table.leave(player);
        table.broadCastWithout("onPlayerLeave",
            {uid: player.getUid()}, player.getUid());
    }
    return {resultCode: 0, message: "ok" };
};

Service.prototype.notifyPlayerOnline = function (frontendId, sessionId, uid) {
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        logger.error("notifyPlayerOnline. frontendId=" + frontendId + " sessionId=" +
            sessionId + " uid=" + uid);
        return;
    }
    let player = this.findPlayerByUid(uid);
    if (player != null) {
        logger.info("player reconnect. uid=" + uid +
            " prevFrontendId=" + player.getFrontendId() +
            " prevSessionId=" + player.getSessionId() +
            " frontendId=" + player.getFrontendId() +
            " sessionId=" + sessionId);
        player.refreshConnectionInfo(frontendId, sessionId);
        let table = this.findTableByPlayer(frontendId, sessionId);
        if (table != null) {
            table.broadCast("onPlayerOnline", {uid: player.getUid()});
            this.doPlayerReconnect(table, player);
        }
    }
    return player;
};

Service.prototype.notifyPlayerOffline = function (frontendId, sessionId) {
    let connectionId = this.buildConnectionId(frontendId, sessionId);
    if (this.connectionState.hasOwnProperty(connectionId)) {
        delete this.connectionState[connectionId];
    }
    let table = this.findTableByPlayer(frontendId, sessionId);
    if (!table) {
        return;
    }
    let player = table.findPlayer(frontendId, sessionId);
    if (!player) {
        return;
    }
    if (player.isReady()) {
        player.setOffline();
        table.broadCastWithout("onPlayerOffline", {uid: player.getUid()}, player.getUid());
    } else {
        table.leave(player);
        table.broadCastWithout("onPlayerLeave", {uid: player.getUid()}, player.getUid());
    }
};

Service.prototype.getReady = function (frontendId, sessionId) {
    let table = this.findTableByPlayer(frontendId, sessionId);
    if (table == null) {
        logger.error("getReady failed... for table does not exist." +
        " frontendId=" + frontendId + " sessionId=" + sessionId);
        return { resultCode: 404, message: "桌子不存在" };
    }
    let player = this.findPlayer(frontendId, sessionId);
    if (player == null) {
        logger.error("getReady failed... for player does not exist." +
            " frontendId=" + frontendId + " sessionId=" + sessionId);
        return { resultCode: 404, message: "玩家不在线"};
    }
    if (table.getIsPlaying()) {
        logger.error("getReady failed... for player is playing." +
            " frontendId=" + frontendId + " sessionId=" + sessionId);
        return { resultCode: 404, message: "牌局正在进行中，无法准备" }
    }
    if (table.setReady(player)) {
        table.broadCast("onPlayerGotReady", {
            uid: player.getUid()
        });
        return {resultCode: 0, message: "ok"};
    } else {
        return {
            resultCode: 404,
            message: "准备失败，玩家不适合当前场次"
        };
    }
};

Service.prototype.sendChatMessage = function (frontendId, sessionId, message) {
    let table = this.findTableByPlayer(frontendId, sessionId);
    if (!table) {
        logger.error("sendChatMessage failed for table does not exist. frontendId=" +
            frontendId + " sessionId=" + sessionId);
        return {resultCode: 404, message: "桌子不存在"};
    }
    let player = table.findPlayer(frontendId, sessionId);
    if (!player) {
        logger.error("sendChatMessage failed for player does not exist" +
        " frontendId=" + frontendId + " sessionId=" + sessionId);
        return {resultCode: 404, message: "玩家不存在"};
    }
    table.notifyMessage({uid: player.account.uid, message: message});
};

Service.prototype.getYuanBaoRoomByMultiplier = function (multiplier) {
    let rooms = this.app.get("rooms");
    for (let room of rooms) {
        if (room.multiplier === multiplier) {
            return room;
        }
    }
    return null;
};

Service.prototype.getSystemNumOfPlayersByRid = function (rid) {
    let systemRoomNumOfPlayers = 0;
    this.tables.forEach(function (table) {
        if (table.getCreatorUid() === 0 && table.getRoom().rid == rid) {
            systemRoomNumOfPlayers += table.getNumOfPlayers();
        }
    });
    return systemRoomNumOfPlayers;
};

Service.prototype.getVisiblePlayerYuanBaoTablesByRid = function (rid) {
    let visiblePlayerTables = [];
    this.tables.forEach(function (table) {
        if (table.isVisible() && table.getCreatorUid() !== 0 && table.getRoom().rid == rid)  {
            visiblePlayerTables.push(table);
        }
    });
    return visiblePlayerTables;
};

Service.prototype.getNumOfSparePosition = function (rid) {
    let visibleTables = this.getVisiblePlayerYuanBaoTablesByRid(rid);
    let count = 0;
    for (let i = 0; i < visibleTables.length; i++) {
        let table = visibleTables[i];
        count = count + table.getNumOfSparePosition();
    }
    return count;
};

Service.prototype.findPlayer = function (frontendId, sessionId) {
    for (let table of this.tables) {
        let player = table.findPlayer(frontendId, sessionId);
        if (!!player) {
            return player;
        }
    }
    return null;
};

Service.prototype.findPlayerByUid = function (uid) {
    for (let table of this.tables) {
        let player = table.findPlayerByUid(uid);
        if (!!player) {
            return player;
        }
    }
    return null;
};

Service.prototype.getPlayerTableByCode = function (code) {
    for(let table of this.tables) {
        if(table.getCreatorUid() !== 0 && table.getCode() === code) {
            return table;
        }
    }
    return null;
};

Service.prototype.getFirstSpareSystemTableByRid = function (rid) {
    for(let table of this.tables) {
        if(table.getCreatorUid() === 0 && table.getRoom().rid == rid && !table.isFull()) {
            return table;
        }
    }
    return null;
};

Service.prototype.findTableByPlayer = function (frontendId, sessionId) {
    for (let table of this.tables) {
        if (table.findPlayer(frontendId, sessionId)) {
            return table;
        }
    }
    return null;
};

Service.prototype.getSystemYuanBaoRooms = function () {
    let rooms = this.app.get("rooms");
    let systemYuanBaoRooms = [];
    for(let room of rooms) {
        if (room.type === 1) {
            room["numOfPlayers"] = this.getSystemNumOfPlayersByRid(room.rid);
            systemYuanBaoRooms.push(room);
        }
    }
    return systemYuanBaoRooms;
};



Service.prototype.tick = function () {
    //1. 桌子tick
    this.tables.forEach(function (table) {
        table.tick();
    });
    //2. 删除invalid桌子
    let invalidCodes = [];
    let newTables = [];
    this.tables.forEach(function (table) {
        if (!table.isValid()) {
            if (table.getCode().length === 6) {
                invalidCodes.push(table.getCode());
            }
        } else {
            newTables.push(table);
        }
    });
    //3. 回收tableCode
    this.tables = newTables;
    if (invalidCodes.length > 0) {
        logger.info("clear table. code=" + JSON.stringify(invalidCodes));
        this.app.rpc.account.tableRemote.clearCode(null, {codes: invalidCodes}, function () {
        });
    }
};

Service.prototype.getGameId = function () {
    return this.gid;
};

Service.prototype.compareTable = function(t1, t2) {
    if (t1.getNumOfPlayers() - t2.getNumOfPlayers() < 0) {
        return -1;
    } else if (t1.getNumOfPlayers() - t2.getNumOfPlayers() > 0) {
        return 1;
    }
    return t1.getCreateTime() - t2.getCreateTime() > 0 ? 1 : -1;
};

Service.prototype.notifyPlayerBalanceChange = function (frontendId, sessionId, amount) {
     let player = this.findPlayer(frontendId, sessionId);
     if (!player) {
         return;
     }
     player.changeBalance(amount);
     this.broadCast("onPlayerBalanceChange", {
         uid: player.getUid(),
         balance: player.getBalance()
     });
};

Service.prototype.getVisibleYuanBaoTables = function () {
    this.tables.sort(this.compareTable);
    let serializeTables = [];
    this.tables.forEach(function (table) {
        if (table.isVisible()) {
            serializeTables.push(table.toYuanBaoTable());
        }
    });
    return serializeTables;
};

Service.prototype.getVisiblePlayerYuanBaoTables = function () {
    let tables = [];
    this.tables.forEach(function (table) {
        if (table.isVisible() && table.getCreatorUid() !== 0) {
            tables.push(table);
        }
    });
    tables.sort(this.compareTable);
    let serializeTables = [];
    tables.forEach(function (table) {
        serializeTables.push(table.toYuanBaoTable());
    });
    return serializeTables;
};

Service.prototype.doCreatePlayer = function (playerData, frontendId, sessionId) {
    throw new Error("need override by child class");
};

Service.prototype.doValidateCreateTableParams = function (params) {
    throw new Error("need override by child class");
};

Service.prototype.doCreateYuanBaoTable  = function (data) {
    throw new Error("need override by child class");
};

Service.prototype.doPlayerReconnect = function (table, player) {
    throw new Error("need override by child class");
};

Service.prototype.doCreateSystemTable = function (room) {
    throw new Error("need override by child class");
};

module.exports = Service;
